package com.whnk.attendance.service.impl;

import com.whnk.attendance.constant.AttendanceConstants;
import com.whnk.attendance.domain.*;
import com.whnk.attendance.mapper.*;
import com.whnk.attendance.service.IMonthEmpStatisService;
import com.whnk.attendance.api.vo.MonthEmpStatisBatchQueryVo;
import com.whnk.attendance.vo.MonthEmpStatisListVo;
import com.whnk.attendance.vo.MonthEmpStatisQueryVo;
import com.whnk.common.core.utils.DateUtils;
import com.whnk.common.core.utils.SpringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 每月统计（员工口径）Service业务层处理
 *
 * @author whnk
 * @date 2021-11-17
 */
@Service
public class MonthEmpStatisServiceImpl implements IMonthEmpStatisService {
    private static final Logger logger = LoggerFactory.getLogger(MonthEmpStatisServiceImpl.class);

    @Autowired
    private MonthEmpStatisMapper monthEmpStatisMapper;

    @Autowired
    private PmSignInfoMapper pmSignInfoMapper;

    @Autowired
    private SocialInsuranceInfoMapper socialInsuranceInfoMapper;

    @Autowired
    private ProjectClassifyInfoMapper projectClassifyInfoMapper;

    @Autowired
    private PmProInfoMapper pmProInfoMapper;

    @Autowired
    private ProjectEmpInfoMapper projectEmpInfoMapper;

    @Autowired
    private PmProLevelMapper pmProLevelMapper;

    @Autowired
    private EmpBaseInfoMapper empBaseInfoMapper;

    /**
     * 查询每月统计（员工口径）
     *
     * @param empStatisId 每月统计（员工口径）主键
     * @return 每月统计（员工口径）
     */
    @Override
    public MonthEmpStatis selectMonthEmpStatisByEmpStatisId(Integer empStatisId) {
        return monthEmpStatisMapper.selectMonthEmpStatisByEmpStatisId(empStatisId);
    }

    /**
     * 查询每月统计（员工口径）列表
     *
     * @param monthEmpStatis 每月统计（员工口径）
     * @return 每月统计（员工口径）
     */
    @Override
    public List<MonthEmpStatis> selectMonthEmpStatisList(MonthEmpStatis monthEmpStatis) {
        return monthEmpStatisMapper.selectMonthEmpStatisList(monthEmpStatis);
    }

    /**
     * 新增每月统计（员工口径）
     *
     * @param monthEmpStatis 每月统计（员工口径）
     * @return 结果
     */
    @Override
    public int insertMonthEmpStatis(MonthEmpStatis monthEmpStatis) {
        monthEmpStatis.setCreateTime(DateUtils.getNowDate());
        return monthEmpStatisMapper.insertMonthEmpStatis(monthEmpStatis);
    }

    /**
     * 修改每月统计（员工口径）
     *
     * @param monthEmpStatis 每月统计（员工口径）
     * @return 结果
     */
    @Override
    public int updateMonthEmpStatis(MonthEmpStatis monthEmpStatis) {
        monthEmpStatis.setUpdateTime(DateUtils.getNowDate());
        return monthEmpStatisMapper.updateMonthEmpStatis(monthEmpStatis);
    }

    /**
     * 批量删除每月统计（员工口径）
     *
     * @param empStatisIds 需要删除的每月统计（员工口径）主键
     * @return 结果
     */
    @Override
    public int deleteMonthEmpStatisByEmpStatisIds(Integer[] empStatisIds) {
        return monthEmpStatisMapper.deleteMonthEmpStatisByEmpStatisIds(empStatisIds);
    }

    /**
     * 删除每月统计（员工口径）信息
     *
     * @param empStatisId 每月统计（员工口径）主键
     * @return 结果
     */
    @Override
    public int deleteMonthEmpStatisByEmpStatisId(Integer empStatisId) {
        return monthEmpStatisMapper.deleteMonthEmpStatisByEmpStatisId(empStatisId);
    }

    @Override
    public List<MonthEmpStatisListVo> queryMonthEmpStatis(MonthEmpStatisQueryVo monthEmpStatisQueryVo) {
        List<MonthEmpStatisListVo> pmSignInfos = monthEmpStatisMapper.queryMonthEmpStatis(monthEmpStatisQueryVo);
        return pmSignInfos;
    }

    @Override
    public void batchComputer(MonthEmpStatisBatchQueryVo monthEmpStatisBatchQueryVo){
        logger.info("***每月统计员工口径开始***");
        Date startTime = new Date();
        try{
            SpringUtils.getBean(IMonthEmpStatisService.class).batchComputerInner(monthEmpStatisBatchQueryVo);
        }catch(Exception ex){
            throw ex;
        }finally {
            Date endTime = new Date();
            long usedTime = (endTime.getTime()- startTime.getTime())/1000;
            String msg = String.format("***每月统计员工口径结束,耗时%1$s,单位秒***", usedTime);
            logger.info(msg);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchComputerInner(MonthEmpStatisBatchQueryVo monthEmpStatisBatchQueryVo) {
        /**
         * 1.根据开始日期,结束日期查询考勤表pm_sign_info
         *   如果有数据,删除统计（员工口径）month_emp_statis
         *   如果没有数据，程序结束
         */
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM");
        List<PmSignInfo> pmSignInfos = pmSignInfoMapper.selectPmSignInfoByTime(monthEmpStatisBatchQueryVo);
        if (CollectionUtils.isEmpty(pmSignInfos)) {
            String msg = String.format("考勤没有数据.开始时间%1$s,结束时间%2$s", monthEmpStatisBatchQueryVo.getStartMonth(), monthEmpStatisBatchQueryVo.getEndMonth());
            logger.info(msg);
            return;
        } else {
            monthEmpStatisMapper.deletePmSignInfoByTime(monthEmpStatisBatchQueryVo);
        }

        /**
         * 公共取数逻辑
         * 社保
         * build map1 key:社保分类编号 value:社保对象
         */
        Map<String, SocialInsuranceInfo> socialInsuranceInfoMap = new HashMap<>();
        SocialInsuranceInfo socialInsuranceInfo = new SocialInsuranceInfo();
        List<SocialInsuranceInfo> socialInsuranceInfos = socialInsuranceInfoMapper.selectSocialInsuranceInfoList(socialInsuranceInfo);
        for (SocialInsuranceInfo insuranceInfo : socialInsuranceInfos) {
            socialInsuranceInfoMap.put(insuranceInfo.getSocialInsNum(), insuranceInfo);
        }

        /**
         * 公共取数逻辑
         * 项目分类列表
         * build map1 key:项目分类编号 value:项目分类对象
         */
        Map<String, ProjectClassifyInfo> projectClassifyInfoMap = new HashMap<>();
        ProjectClassifyInfo projectClassifyInfo = new ProjectClassifyInfo();
        List<ProjectClassifyInfo> projectClassifyInfos = projectClassifyInfoMapper.selectProjectClassifyInfoList(projectClassifyInfo);
        for (ProjectClassifyInfo classifyInfo : projectClassifyInfos) {
            projectClassifyInfoMap.put(classifyInfo.getProClaNum(), classifyInfo);
        }

        /**
         * 公共取数逻辑
         * 项目列表
         * build map1 key:项目编号 value:项目列表对象
         */
        Map<String, PmProInfo> proInfoMap = new HashMap<>();
        PmProInfo pmProInfo = new PmProInfo();
        List<PmProInfo> pmProInfos = pmProInfoMapper.selectPmProInfoList(pmProInfo);
        for (PmProInfo proInfo : pmProInfos) {
            proInfoMap.put(proInfo.getProNum(), proInfo);
        }

        /**
         * 公共取数逻辑
         * 项目人员列表project_emp_info:
         * build map1 key:项目编号+身份证号+评级日期 value:项目人员列表对象
         * build map2 key:项目编号+身份证号 value:最新的项目人员列表对象
         */
        Map<String, ProjectEmpInfo> projectEmpInfoMap = new HashMap<>();
        Map<String, ProjectEmpInfo> projectEmpInfoMapLatest = new HashMap<>();
        ProjectEmpInfo pmProjectEmpInfo = new ProjectEmpInfo();
        List<ProjectEmpInfo> projectEmpInfos = projectEmpInfoMapper.selectProjectEmpInfoList(pmProjectEmpInfo);
        for (ProjectEmpInfo projectEmpInfo : projectEmpInfos) {
            String rankTime = dateFormat.format(projectEmpInfo.getRankTime());
            String key = projectEmpInfo.getProNum() + projectEmpInfo.getEmpId() + rankTime;
            projectEmpInfoMap.put(key, projectEmpInfo);
            String keyLatest = projectEmpInfo.getProNum() + projectEmpInfo.getEmpId();
            ProjectEmpInfo projectEmpInfoExist = projectEmpInfoMapLatest.get(keyLatest);
            if(projectEmpInfoExist != null){
                Date oldTime = projectEmpInfoExist.getRankTime();
                Date currentTime = projectEmpInfo.getRankTime();
                if(currentTime.compareTo(oldTime) > 0){
                    projectEmpInfoMapLatest.put(keyLatest, projectEmpInfo);
                }

            }else{
                projectEmpInfoMapLatest.put(keyLatest, projectEmpInfo);
            }
        }


        /**
         * 公共取数逻辑
         * 项目级别列表pm_pro_level:
         * build map1 key:项目编号，行方评级编号 value:项目级别对象
         */
        Map<String, PmProLevel> pmProLevelHashMap = new HashMap<>();
        PmProLevel pmProLevel = new PmProLevel();
        List<PmProLevel> proLevels = pmProLevelMapper.selectPmProLevelList(pmProLevel);
        for (PmProLevel proLevel : proLevels) {
            pmProLevelHashMap.put(proLevel.getProNum()+proLevel.getBankLevelNum(), proLevel);
        }

        /**
         * 公共取数逻辑
         * 人员基本信息emp_base_info:
         * build map1 key:身份证号 value:人员基本信息对象
         */
        Map<String, EmpBaseInfo> empBaseInfoHashMap = new HashMap<>();
        EmpBaseInfo empBaseInfo = new EmpBaseInfo();
        List<EmpBaseInfo> empBaseInfos = empBaseInfoMapper.selectEmpBaseInfoList(empBaseInfo);
        for (EmpBaseInfo baseInfo : empBaseInfos) {
            empBaseInfoHashMap.put(baseInfo.getEmpId(), baseInfo);
        }


        /**
         * 2.遍历考勤表pm_sign_info list,构造数据,保存month_emp_statis
         *
         */
        List<MonthEmpStatis> monthEmpStatisList = new ArrayList<>();
        for(PmSignInfo pmSignInfo:pmSignInfos){
            MonthEmpStatis monthEmpStatis = new MonthEmpStatis();
            /**
             * 直接获取获取身份证,姓名,隶属项目,月份
             */
            monthEmpStatis.setEmpId(pmSignInfo.getEmpId());
            monthEmpStatis.setEmpNm(pmSignInfo.getEmpNm());
            monthEmpStatis.setProNum(pmSignInfo.getProSn());
            monthEmpStatis.setProName(pmSignInfo.getProName());
            monthEmpStatis.setMonth(dateFormat.format(pmSignInfo.getSignDt()));
            /**
             * 出勤人天
             * 考勤表，字段：驻场办公
             */
            monthEmpStatis.setAttendanceDay(pmSignInfo.getProOnOffice());
            /**
             * 行方评级
             * 查询条件：人员，项目，月份，查询项目人员表，
             *   如果日期对上，取对上的
             *   如果没有对上，取最新的一条
             *
             */

            String key = monthEmpStatis.getProNum() + monthEmpStatis.getEmpId() + monthEmpStatis.getMonth();
            String keyLatest = monthEmpStatis.getProNum() + monthEmpStatis.getEmpId();
            ProjectEmpInfo projectEmpInfo = projectEmpInfoMap.get(key);
            if(null == projectEmpInfo){
                projectEmpInfo = projectEmpInfoMapLatest.get(keyLatest);
            }
            //如果没有取到行方评级编号,后面字段不计算,直接插入数据
            if(null == projectEmpInfo){
                String msg = String.format("身份证%1$s,姓名%2$s,隶属项目%3$s,月份%4$s,查询项目人员列表,没有查到行方评级编号",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm(),
                        monthEmpStatis.getProNum(),monthEmpStatis.getMonth());
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }
            String bankLevelNum = projectEmpInfo.getBankLevelNum();
            monthEmpStatis.setBankLevelNum(bankLevelNum);
            /**
             * 级别单价
             * 查询项目级别列表pm_pro_level，
             * 查询条件：项目编号，行方评级编号,获取项目级别对象
             */

            key = monthEmpStatis.getProNum()+bankLevelNum;
            PmProLevel proLevel = pmProLevelHashMap.get(key);
            //如果没有取到项目级别对象,后面字段不计算,直接插入数据
            if(null == proLevel){
                String msg = String.format("身份证%1$s,姓名%2$s,隶属项目%3$s,月份%4$s,行方评级编号%5$s,查询项目级别列表,没有查到项目级别对象",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm(),
                        monthEmpStatis.getProNum(),monthEmpStatis.getMonth(),
                        bankLevelNum);
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }

            monthEmpStatis.setBankLevelName(proLevel.getBankLevelName());
            String proPriceState = proLevel.getProPriceState();
            if(AttendanceConstants.PRO_LEVEL_PRICE_STATE_Y.equals(proPriceState)){
                monthEmpStatis.setProPrice(proLevel.getProDayPrice());
            }else{
                monthEmpStatis.setProPrice(proLevel.getProMonthPrice());
            }


            /**
             * 收入预估
             * 按照天：日级别单价*出勤天数
             * 按照月：月级别单价*出勤天数/人月折算天数
             */
            BigDecimal incomeForecast = null;
            if(AttendanceConstants.PRO_LEVEL_PRICE_STATE_Y.equals(proPriceState)){

                incomeForecast = proLevel.getProDayPrice().multiply(monthEmpStatis.getAttendanceDay());
            }else{
                incomeForecast = proLevel.getProMonthPrice().multiply(monthEmpStatis.getAttendanceDay()).divide(proLevel.getProMonthDay(),AttendanceConstants.MONTH_EMP_STATIS_SCALE);
            }
            monthEmpStatis.setIncomeForecast(incomeForecast.setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));

            /**
             * 基本工资
             * 人员基本信息
             * 字段：基本工资
             */
            EmpBaseInfo baseInfo = empBaseInfoHashMap.get(monthEmpStatis.getEmpId());
            if(null == baseInfo){
                String msg = String.format("身份证%1$s,姓名%2$s,查询人员基本信息,没有查到",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm());
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }
            monthEmpStatis.setBasicWage(baseInfo.getEmpWage().setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));

            /**
             * 五险一金
             * 计算逻辑：社保+公积金+服务费
             * 社保：
             *   基本工资*30% A,>社保基数,结果：基本工资*30% *社保公付比例
             *   基本工资*30% A,<=社保基数,结果：社保基数 *社保公付比例
             * 公积金：
             *   基本工资*30% A,>公积金基数,结果：基本工资*30% *公积金公付比例
             *   基本工资*30% A,<=公积金基数,结果：公积金基数 *公积金公付比例
             * 服务费：
             *   固定服务费字段
             */
            PmProInfo proInfo = proInfoMap.get(monthEmpStatis.getProNum());
            if(null == proInfo){
                String msg = String.format("身份证%1$s,姓名%2$s,隶属项目%3$s,查询项目列表,没有查到",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm(),
                        monthEmpStatis.getProNum());
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }
            ProjectClassifyInfo classifyInfo = projectClassifyInfoMap.get(proInfo.getProClaNum());
            if(null == classifyInfo){
                String msg = String.format("身份证%1$s,姓名%2$s,隶属项目%3$s,项目分类编号%4$s,查询项目分类列表,没有查到",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm(),
                        monthEmpStatis.getProNum(),proInfo.getProClaNum());
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }
            SocialInsuranceInfo insuranceInfo = socialInsuranceInfoMap.get(classifyInfo.getSocialInsNum());
            if(null == insuranceInfo){
                String msg = String.format("身份证%1$s,姓名%2$s,隶属项目%3$s,社保分类编号%4$s,查询社保,没有查到",
                        monthEmpStatis.getEmpId(), monthEmpStatis.getEmpNm(),
                        monthEmpStatis.getProNum(),classifyInfo.getSocialInsNum());
                logger.error(msg);
                monthEmpStatisList.add(monthEmpStatis);
                continue;
            }
            BigDecimal shebao = null;
            BigDecimal gjj = null;
            BigDecimal fwf = insuranceInfo.getServiceCharge();
            BigDecimal discount = BigDecimal.valueOf(0.3);
            BigDecimal empWage = baseInfo.getEmpWage();
            BigDecimal socialInsBase = insuranceInfo.getSocialInsBase();
            BigDecimal housingFundBase = insuranceInfo.getHousingFundBase();
            if(empWage.multiply(discount).compareTo(socialInsBase) > 0){
                shebao = empWage.multiply(discount).multiply(insuranceInfo.getSocialInsRatio());
            }else{
                shebao = socialInsBase.multiply(insuranceInfo.getSocialInsRatio());
            }
            if(empWage.multiply(discount).compareTo(housingFundBase) > 0){
                gjj = empWage.multiply(discount).multiply(insuranceInfo.getHousingFundRatio());
            }else{
                gjj = housingFundBase.multiply(insuranceInfo.getHousingFundRatio());
            }
            BigDecimal insuranceFund = shebao.add(gjj).add(fwf).setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP);
            monthEmpStatis.setInsuranceFund(insuranceFund);

            /**
             * 公摊
             * 项目分类列表
             * 字段：公摊系数
             */
            monthEmpStatis.setPublicCost(classifyInfo.getPublicCost().setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));

            /**
             * 补助绩效
             * 补助+绩效：
             * 补助：考勤表，字段：总计补助金额（元）
             * 绩效：基本工资*0.05
             */
            BigDecimal buzhu = pmSignInfo.getSurAmountTotal();
            if(null == buzhu){
                buzhu = BigDecimal.valueOf(0);
            }
            BigDecimal jixiao = empWage.multiply(BigDecimal.valueOf(0.05));
            monthEmpStatis.setSubPerformance(buzhu.add(jixiao).setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));

            /**
             * 人力成本预估
             * 基本工资+五险一金+公摊+补助绩效
             */
            BigDecimal renliyusuan = monthEmpStatis.getBasicWage().add(monthEmpStatis.getInsuranceFund())
                    .add(monthEmpStatis.getPublicCost()).add(monthEmpStatis.getSubPerformance());
            monthEmpStatis.setHumenCostForecast(renliyusuan.setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));

            /**
             * 毛 利
             * 收入预估-人力成本预估-收入预估*6.37%
             */
            BigDecimal maoli = monthEmpStatis.getIncomeForecast().subtract(monthEmpStatis.getHumenCostForecast())
                    .subtract(monthEmpStatis.getIncomeForecast().multiply(BigDecimal.valueOf(0.0637)));
            monthEmpStatis.setGrossProfit(maoli.setScale(AttendanceConstants.MONTH_EMP_STATIS_SCALE, BigDecimal.ROUND_HALF_UP));
            /**
             * 毛利率
             * 毛 利/收入预估
             */
            BigDecimal maolilv = BigDecimal.valueOf(0);
            if(monthEmpStatis.getIncomeForecast().compareTo(BigDecimal.ZERO) != 0){
                maolilv = monthEmpStatis.getGrossProfit().divide(monthEmpStatis.getIncomeForecast(),AttendanceConstants.MONTH_EMP_STATIS_SCALE);
            }
            monthEmpStatis.setGrossProfitMargin(maolilv);
            monthEmpStatisList.add(monthEmpStatis);

        }
        /*for(MonthEmpStatis monthEmpStatis:monthEmpStatisList){
            monthEmpStatisMapper.insertMonthEmpStatis(monthEmpStatis);
        }*/

        int limit = AttendanceConstants.BATCH_INSERT_SIZE;
        int listSize = monthEmpStatisList.size();
        List<MonthEmpStatis> newList = new ArrayList<>();
        for (int i = 0; i < listSize; i++) {
            //分批次处理,循环将数据填入载体list
            newList.add(monthEmpStatisList.get(i));
            //临时存储newList达到要求或者达到最大量,进行批量操作
            if (limit == newList.size() || i == listSize - 1) {
                //调用批量插入
                monthEmpStatisMapper.insertMonthEmpStatisBatch(newList);
                //每次批量操作后,清空载体list,等待下次的数据填入
                newList.clear();
            }
        }

    }

    @Override
    @Async("back_cron_job")
    public void batchComputerAsync(MonthEmpStatisBatchQueryVo monthEmpStatisBatchQueryVo) {

        SpringUtils.getBean(IMonthEmpStatisService.class).batchComputer(monthEmpStatisBatchQueryVo);
    }
}
